from utils.copy import fetch_copy


class Container(object):
    """This class represents the "item container" part of a crane, that is, it deals with 
    maintaining a list of items at each crane."""
    CAPACITY_EXCEEDED = "capacity exceeded"
    UNEXPECTED_ITEMS  = "unexpected items found"
    
    def __init__(self, capacity=1, items=()):
        self.capacity = capacity
        self.items = list(items)
        if len(self.items) > self.capacity:
            raise ValueError(Container.CAPACITY_EXCEEDED)
            
    def __deepcopy__(self, memo):
        clone = fetch_copy(self, memo)
        clone.capacity = self.capacity
        clone.items = list(self.items)
        return clone
        
    @property
    def is_empty(self):
        return len(self.items) == 0
        
    @property
    def is_full(self):
        return len(self.items) >= self.capacity
        
    def can_execute(self, action):
        return self._can_execute(action) is None
        
    def _can_execute(self, action):
        """Checks whether the container can execute the argument action. Returns None if the 
        action can be executed, otherwise returns a string containing an error message."""
        if action.is_pickup:
            if len(self.items) + len(action.items) > self.capacity:
                return Container.CAPACITY_EXCEEDED
        elif action.is_putdown:
            last_items = self.items[-len(action.items):]
            last_items.reverse()
            if last_items != action.items:
                return Container.UNEXPECTED_ITEMS
        else:
            raise TypeError("unexpected action type")
        return None
        
    def execute(self, action):
        if action.is_pickup:
            assert len(self.items) + len(action.items) <= self.capacity
            for item in action.items:
                self.append(item)
        elif action.is_putdown:
            for item in action.items:
                assert item == self.items[-1]
                self.pop()
        else:
            raise TypeError("unexpected action type")
            
    def append(self, item):
        self.items.append(item)
        
    def pop(self):
        return self.items.pop()
        
        
